﻿<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet
  version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:html="http://www.w3.org/1999/xhtml"
  xmlns:jsx="http://www.codeplex.com/JSClientPages/Compiler"
  xmlns:js="http://www.codeplex.com/JSClientPages/JSElements">
    <!--
        DiXon Generation 1
        
        JavaScript Client Pages Compiler - written in XSLT 1.0
        
        This XSLT stylesheet translates a mixed-namespace XHTML page with
        embedded JavaScript processing instructions (e.g. <?js alert('Yes!'); ?>),
        as well as custom tags that translate to JavaScript code.
        
        Requirements:
        
        - A standards-compliant XSLT processor
        - Support for the document() function
        - A correct implementation of document('');
        
        Copyright (c) 2008, Artem Ploujnikov
        All rights reserved.

            Redistribution and use in source and binary forms, with or without modification, are permitted provided 
            that the following conditions are met:

        * Redistributions of source code must retain the above copyright notice, this list of conditions and 
        the following disclaimer.

        * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 
        the following disclaimer in the documentation and/or other materials provided with the distribution.

        * Neither the name of Artem Ploujnikov nor the names of its contributors may be used to endorse or promote 
        products derived from this software without specific prior written permission.

        THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 
        WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 
        PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 
        ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
        LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 
        OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 
        STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 
        THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
    -->

    <xsl:output method="text" standalone="yes"/>

    <xsl:include href="DiXon-TextReplace.xslt"/>

    <!-- 
        The default template copies the current node and applies
        all templates to its children
    -->
    <xsl:template match="@* | node()">
        <xsl:copy>
            <xsl:apply-templates select="@* | node()"/>
        </xsl:copy>
    </xsl:template>


    <!--
        The HTML header will not be processed directly
    -->
    <xsl:template match="html:head/node()"></xsl:template>


    <!--
        The template for the root element.
        
        This template takes care of space normalization for the
        final output        
    -->
    <xsl:template match="html:html">
        <xsl:variable name="result">
            <xsl:apply-templates select="html:body" />
        </xsl:variable>
        <xsl:value-of select="normalize-space($result)"/>
    </xsl:template>

    <!--
       The generic HTML element handler. 
       
       HTML elements are converted to imperative
       DOM-generating code; more specifically, an indirect
       call to DOMWriter::startElement(); and DOMWriter::endElement()
    -->
    <xsl:template match="*">
        se('<xsl:value-of select="local-name()"/>');
        <xsl:apply-templates select="@*|node()"/>
        ee();
    </xsl:template>

    <!--
        The generic HTML attribute handler
        
        HTML attributes are converted to imperative DOM-generating
        code; more specifically, an indirect call to DOMWriter::writeAttribute()
    -->
    <xsl:template match="@*">
        a('<xsl:value-of select="local-name()"/>','<xsl:value-of select="current()" />');
    </xsl:template>

    <!--
        The default plain text handler. 
        
        All characters that cannot appear in JavaScript strings are escaped,
        and then all space is normalized for conciseness.
    -->
    <xsl:template match="text()">
    	<xsl:variable name="normalized-text" select="normalize-space(.)" />
    	<xsl:if test="string-length($normalized-text) &gt; 0">
	        <xsl:variable name="text-value">
	            <xsl:if test="starts-with(., ' ')">
	                <xsl:value-of select="' '"/>
	            </xsl:if>
	            <xsl:call-template name="replace-context">
	                <xsl:with-param name="context">js-text</xsl:with-param>
	                <xsl:with-param name="text" select="$normalized-text" />
	            </xsl:call-template>
	            <xsl:if test="substring(., string-length(.)) = ' '">
	                <xsl:value-of select="' '"/>
	            </xsl:if>
	        </xsl:variable>
	        <xsl:if test="string-length($text-value) > 0">
	            t('<xsl:value-of select="$text-value"/>');
	        </xsl:if>
	    </xsl:if>
    </xsl:template>

    <!--
        The "js" processing instruction handler for HTML-embedded
        JavaScript code.
        
        Any JavaScript contained in the processing instructions is output verbatim.
    -->
    <xsl:template match="processing-instruction()[local-name() = 'js']">
        <xsl:value-of select="string(.)" disable-output-escaping="yes" />
    </xsl:template>

    <!--
        The implementation for the <js:forEach> tag that makes it easier
        to iterate over JavaScript collections
        
        Example:
        <js:forEach collection="input.list" value="item">
            <div class="item">
                <js:output value="item.description" />
                <span class="price">
                    (Price: <js:output value="formatCurrency(item.price)" />)
                </span>
            </div>
        </js:forEach>
    
    -->
    <xsl:template match="js:forEach">
        fe(
        <xsl:value-of select="@collection"/>,
        '<xsl:value-of select="@item"/>',
        function($ic) {
            with($ic) {
            <xsl:apply-templates />
            }
        }
        );
    </xsl:template>


    <!-- 
        The compiler template for the <js:output> element.
                
        @value - the value to be output - any JavaScript expression
        
        Example: 
            <js:output value="input.firstName">
       
        This is equivalent to
        <?js text(input.firstName); ?>
    -->
    <xsl:template match="js:output">
        t(<xsl:value-of select="@value"/>);
    </xsl:template>

    <!--
        The compiler template for the <js:condition> element
        
        This element translates to a simple if...else condition.
        
        Example:
        
        <js:condition>
            <js:if>value &gt; 100</js:if>
            <js:then><span class="good">a lot</span></js:then>
            <js:else><span class="bad">a little</span></js:else>
        </js:condition>
        
        This will translate into the equivalent of:
        
        if (value > 100) {
            writer.startElement('span');
            writer.writeAttribute('class', 'good');
            writer.writeText('a lot');
            writer.endElement();
        } else {
            writer.startElement('span');
            writer.writeAttribute('class', 'bad');
            writer.writeText('a little');
            writer.endElement();
        }             
        
    -->
    <xsl:template match="js:condition">
        if(<xsl:value-of select="js:if"/>) {
        <xsl:apply-templates select="js:then/*" />
        }
        <xsl:if test="js:else">
            else {
            <xsl:apply-templates select="js:else/*" />
            }
        </xsl:if>
    </xsl:template>

    <!--
        The compiler implementation for <js:switch type="simple">, which
        is converted to a regular JavaScript switch statement, which results
        in different code paths being selected depending on the value of the
        test expression
        
        Example:
        
        <js:switch type="simple" value="input.grade">
            <js:case if="'A'">Excellent</js:case>
            <js:case if="'B'">Good</js:case>
            <js:case if="'C'">Fair</js:case>
            <js:case if="'D'">Poor</js:case>
            <js:case if="'F'">Failure</js:case>            
            <js:default>Unknown</js:default>
        </js:switch>
        
        This compiles to the equivalent of
        
        switch(input.grade)
        {
            case 'A':
                text('Excellent');
                break;
            case 'B'
                text('Good');
                break;
            case 'C'
                text('Fair');
                break;
            case 'D'
                text('Poor');
                break;
            case 'F'
                text('Failure');
                break;
            default:
                text('Unknown');
                break;
        }
    -->
    <xsl:template match="js:switch[not(@type) or @type = 'simple']">
        switch(<xsl:value-of select="js:value"/>) {
        <xsl:for-each select="js:case">
            case <xsl:value-of select="@if"/>:
            <xsl:apply-templates />
            break;
        </xsl:for-each>
        <xsl:if test="js:default">
            default:
            <xsl:apply-templates select="js:default/*"/>
        </xsl:if>
        }
    </xsl:template>

    <!--
        The compiler implementation for "complex" switches 
        represented as <js:switch type="complex">, which is
        translated to if ... else if ... else if ... else.
        
        Example:
        
        <js:switch type="complex">
            <js:case if="input.grade &gt; input.average">Above average</js:case>
            <js:case if="input.grade &lt; input.average">Below average</js:case>
            <js:default>Average</js:default>            
        </js:switch>
        
        This translates to
        
        if (input.grade > input.average) {
            text('Above Average');
        } else if (input.grade < input.average) {
            text('Below average');
        } else {
            text('Average');
        }
    -->
    <xsl:template match="js:switch[@type='complex' or not(@value)]">
        <xsl:for-each select="js:case">
            <xsl:if test="position() > 1">else </xsl:if>
            if (<xsl:value-of select="@if"/>) {
            <xsl:apply-templates />
            }
        </xsl:for-each>
        <xsl:if test="js:default">
            else {
            <xsl:apply-templates select="js:default/*"/>
            }
        </xsl:if>
    </xsl:template>


    <!--
        The HTML Body template
        
        This template actually issues the call to JSClientPage.compile(),
        which registers the compiled page and associates it with a name
    -->
    <xsl:template match="html:body">
        <xsl:variable name="meta-name" select="//html:meta[@name='JSClientPage']/@content" />
        <xsl:variable name="name">
            <xsl:choose>
                <xsl:when test="$meta-name">
                    <xsl:value-of select="$meta-name"/>
                </xsl:when>
                <xsl:otherwise>page</xsl:otherwise>
            </xsl:choose>
        </xsl:variable>

        JSClientPage.compile(
        '<xsl:value-of select="$name" />',
        function(writer, input, context) {
            with(context) {
                <xsl:apply-templates select="node()" />
            }
        }
        );
    </xsl:template>
</xsl:stylesheet>
